package aclInfoCollector.filesAnalysers.code.classFile;

import java.io.File;
import java.io.FilenameFilter;
import java.lang.reflect.Method;
import java.util.ArrayList;

import javax.swing.JLabel;

import aclInfoCollector.filesAnalysers.code.CodeFileAnalyser;



import aclInfoCollector.utils.classLoader.ClassLoaderACL;
import aclInfoCollector.utils.extensionsFilters.DotClassFilter;
import aclInfoCollector.utils.specialString.StringOperation;
import aclInfoCollector.utils.specialString.StringParameter;

/**
 * La classe ClassFinder sert a trouver et a parser des fichiers .class.
 * 
 * @version 1.0
 *@author Bendavid Olivier
 *@author Haderer Nicolas
 */

public class ClassFileAnalyser extends CodeFileAnalyser {

	public static void main(String args[]) {
		ClassFileAnalyser cf = new ClassFileAnalyser("fractalexplorer");
		cf.printSummary();
	}

	/**
	 * construit un classFinder initialise les attributs
	 */
	public ClassFileAnalyser() {
		super();
	}

	/**
	 * construit un classFinder initialise les attributs et lance une analyse
	 * sur directoryPath
	 */
	public ClassFileAnalyser(String directoryPath) {
		super(directoryPath);
		launchAnalysis(directoryPath);
	}

	/**
	 * Recherche tous les fichiers .class et les parse
	 * 
	 * @param directoryPath
	 *            est le repertoire dans lequel on souhaite effecuter la
	 *            recherche
	 */
	public void launchAnalysis(String directoryPath) {
		File directoryToScan = new File(directoryPath);
		File[] Files = directoryToScan.listFiles();
		FilenameFilter filter = new DotClassFilter();
		for (int i = 0; i < Files.length; i++) {
			if (Files[i].isDirectory()) {
				launchAnalysis(Files[i].getAbsolutePath());
			}
		}
		File[] FilesClass = directoryToScan.listFiles(filter);
		for (int i = 0; i < FilesClass.length; i++) {
			//System.out.println(FilesClass[i].getName());
			parse(FilesClass[i]);
		}
	}

	/**
	 * parse un fichier .class
	 * 
	 * @param f
	 *            le fichier a parser
	 */
	/**
	 * @param f
	 */
	public void parse(File f) {
		ClassLoaderACL classLoader = new ClassLoaderACL();
		Class<?> c = null;

		try {
			c = classLoader
					.loadClass(f.getAbsolutePath().replace(".class", ""));
			
		} catch (Exception e) {
			//e.printStackTrace();
		}

		if (!(c == null)) {
			if (!c.isInterface()) {
				parseInterfaceType(c);
						}

			else {
				try
				{
					parseClassType(c);
				}
				catch (Exception e) {
					// TODO: handle exception
				}
			}

			
		}
	}
	
	/**
	 * parse le fichier .class dont le nom est s.class
	 * 
	 * @param s
	 *            le nom de la classe a parser
	 */
	public void parse(String s) {
		ClassLoaderACL classLoader = new ClassLoaderACL();
		Class<?> c = null;

		try {
			c = classLoader.loadClass(s.replace(".class", ""));
			classes.add(c.getName());
		} catch (Exception e) {
			e.printStackTrace();
		}

		if (!(c == null)) {
			if (c.isInterface()) {
				parseInterfaceType(c);
			} else {
				parseClassType(c);
			}
		}
	}

	/**
	 * parse une javSource correspondant a une class
	 * 
	 * @param js
	 *            la JavaSource a parser
	 */

	public void parseClassType(Object o) {
		Class<?> c = (Class<?>) o;
		try
		{
			

		
		if (!classesWithSuperTypes.containsKey(c.getName().toString())) {
			classesWithSuperTypes.put(c.getName().toString(),
					new ArrayList<String>());
			classesWithSuperTypes.get(c.getName().toString()).add(
					c.getSuperclass().getName());
			}

		if (!(c.getInterfaces().length == 0)) {
			for (Class<?> i : c.getInterfaces()) {
				if (!classesWithInterfaces.containsKey(c.getName()
						.toString())) {
					classesWithInterfaces.put(c.getName().toString(),
							new ArrayList<String>());
					classesWithInterfaces.get(c.getName().toString())
							.add(i.getName().toString());
				} else {
					classesWithInterfaces.get(c.getName().toString())
							.add(i.getName().toString());
				}
			}
		}
		}
		catch (Exception e) {
			// TODO: handle exception
		}
	}

	/**
	 * parse une javSource correspondant a une interface
	 * 
	 * @param js
	 *            la javasource a parser
	 */
	public void parseInterfaceType(Object o) {
		Class<?> c = (Class<?>) o;
		String inter = c.getName().toString();
		interfaces.add(inter);
		if (c.getInterfaces().length > 0) {
			for (Class<?> cl : c.getInterfaces()) {
				if (!interfacesWithSuperInterFaces.containsKey(c
						.getName().toString())) {
					interfacesWithSuperInterFaces.put(c.getName()
							.toString(), new ArrayList<String>());
					interfacesWithSuperInterFaces.get(
							c.getName().toString()).add(
							cl.getName().toString());
				} else {
					interfacesWithSuperInterFaces.get(
							c.getName().toString()).add(
							cl.getName().toString());
				}
			}
		}

		try {
			if (!(c.getMethods().length == 0)) {
				Method[] methods = c.getDeclaredMethods();

				for (int i = 0; i < methods.length; i++) {
					StringOperation strop = new StringOperation();
					strop.setName(methods[i].getName());

					StringParameter spReturn = new StringParameter();
					spReturn.setName("out");
					spReturn.setType(methods[i].getReturnType()
							.getName());
					strop.setReturnType(spReturn);
					
					System.out.println(strop.getReturnType().getType());

					
					
					if (methods[i].getReturnType().isPrimitive()) {
						if (!primitives.contains(methods[i]
								.getReturnType().getName()))
							primitives.add(methods[i].getReturnType()
									.getName());
					}

					ArrayList<StringParameter> temp = new ArrayList<StringParameter>();
					for (int d = 0; d < methods[i].getParameterTypes().length; d++) {
						StringParameter sp = new StringParameter();
						sp.setType(methods[i].getParameterTypes()[d]
								.getName());
						
						
						if (methods[i].getParameterTypes()[d]
								.isPrimitive()) {
							if (!primitives.contains(methods[i]
									.getParameterTypes()[d].toString()))
								primitives.add(methods[i]
										.getParameterTypes()[d]
										.toString());
						}
						temp.add(sp);
					}
					strop.setParamsTypes(temp);
					if (!interfacesWithOperations.containsKey(inter)) {
						ArrayList<StringOperation> temp2 = new ArrayList<StringOperation>();
						temp2.add(strop);
						interfacesWithOperations.put(inter, temp2);
					} else {
						if (!interfacesWithOperations.get(inter)
								.contains(strop)) {
							interfacesWithOperations.get(inter).add(
									strop);
						}
					}
				}
			}
		} catch (Exception e) {
		} catch (NoClassDefFoundError e) {
		}
		}

	public void launchAnalysis(String directoryPath, JLabel label) {
		File directoryToScan = new File(directoryPath);
		File[] Files = directoryToScan.listFiles();
		FilenameFilter filter = new DotClassFilter();
		for (int i = 0; i < Files.length; i++) {
			if (Files[i].isDirectory()) {
				launchAnalysis(Files[i].getAbsolutePath());
			}
		}
		File[] FilesClass = directoryToScan.listFiles(filter);
		for (int i = 0; i < FilesClass.length; i++) {
			//System.out.println(FilesClass[i].getName());
			parse(FilesClass[i],label);
		}
	}

	public void parse(File f, JLabel label) {
		label.setText(f.getAbsolutePath());
		ClassLoaderACL classLoader = new ClassLoaderACL();
		Class<?> c = null;

		try {
			c = classLoader
					.loadClass(f.getAbsolutePath().replace(".class", ""));
			
		} catch (Exception e) {
			//e.printStackTrace();
		}

		if (!(c == null)) {
			if (c.isInterface()) {
				parseInterfaceType(c);
			}

			}

			else {

				
				parseClassType(c);

			}
	}

	

	
}
